package com.isp.common.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.isp.common.model.exception.APIRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Linsy
 * @version 1.0
 * @created 2024/7/21.
 */
public class JacksonUtils {
    private static Logger logger = LoggerFactory.getLogger(JacksonUtils.class);

    private final static ObjectMapper mapper = new ObjectMapper();

    private final static ObjectMapper noneNullMapper = new ObjectMapper();

    public static final String ROOT_PATH = "$";
    private static final String ROOT_PATH_PRE = ROOT_PATH + ".";

    static {
        initDefaultFeature(mapper);
        initDefaultFeature(noneNullMapper);

        noneNullMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    private static void initDefaultFeature(ObjectMapper mapper) {
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
    }

    public static <T> String toJsonString(T obj) {
        return toJsonString(obj, true);
    }

    public static <T> String toJsonString(T obj, boolean includeNull) {
        ObjectMapper realMapper = includeNull ? mapper : noneNullMapper;
        try {
            return realMapper.writeValueAsString(obj);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            throw new APIRuntimeException("json转换异常", e);
        }
    }

    public static <T> T parseObject(String json, Class<T> clz) {
        try {
            return mapper.readValue(json, clz);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            throw new APIRuntimeException("json转换异常", e);
        }
    }

    public static <T> List<T> parseList(String json, Class<T> clz) {
        try {
            JavaType javaType = constructParametricType(ArrayList.class, clz);
            return (List<T>) mapper.readValue(json, javaType);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            throw new APIRuntimeException("json转换异常", e);
        }
    }

    public static <K, V> Map<K, V> parseMap(String json, Class<K> keyClz, Class<V> valueClz) {
        try {
            JavaType javaType = constructParametricType(LinkedHashMap.class, keyClz, valueClz);
            return (Map<K, V>) mapper.readValue(json, javaType);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            throw new APIRuntimeException("json转换异常", e);
        }
    }

    public static <T> T[] parseArray(String json, Class<T> clz) {
        try {
            JavaType javaType = mapper.getTypeFactory().constructArrayType(clz);
            return (T[]) mapper.readValue(json, javaType);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            throw new APIRuntimeException("json转换异常", e);
        }
    }

    private static JavaType constructParametricType(Class<?> parametrized, Class<?>... elementClasses) {
        return mapper.getTypeFactory().constructParametricType(parametrized, elementClasses);
    }

    /*
     * 将json中指定属性的值转化成java对象
     * path: 如"$.a.b.c" 或 "a.b.c"
     */
    public static <T> T parseByPath(String json, String path, Class<T> clz) {
        JavaType javaType = mapper.getTypeFactory().constructType(clz);
        return parseByPath(json, path, javaType);
    }

    /*
     * 将json中指定属性的值转化成java对象
     * path: 如"$.a.b.c" 或 "a.b.c"
     */
    public static <T> T parseByPath(String json, String path, TypeReference<T> typeReference) {
        JavaType javaType = mapper.getTypeFactory().constructType(typeReference);
        return parseByPath(json, path, javaType);
    }

    /*
     * 将json中指定属性的值转化成List对象
     * path: 如"$.a.b.c" 或 "a.b.c"
     */
    public static <T> List<T> parseListByPath(String json, String path, Class<T> clz) {
        JavaType javaType = constructParametricType(ArrayList.class, clz);
        return parseByPath(json, path, javaType);
    }

    /*
     * 将json中指定属性的值转化成数组对象
     * path: 如"$.a.b.c" 或 "a.b.c"
     */
    public static <T> T[] parseArrayByPath(String json, String path, Class<T> clz) {
        JavaType javaType = mapper.getTypeFactory().constructArrayType(clz);
        return parseByPath(json, path, javaType);
    }

    /*
     * 将json中指定属性的值转化成Map对象
     * path: 如"$.a.b.c" 或 "a.b.c"
     */
    public static <K, V> Map<K, V> parseMapByPath(String json, String path, Class<K> keyClz, Class<V> valueClz) {
        JavaType javaType = constructParametricType(LinkedHashMap.class, keyClz, valueClz);
        return parseByPath(json, path, javaType);
    }

    /*
     * 将json中指定属性的值转化成java对象
     * path: 如"$.a.b.c" 或 "a.b.c"
     */
    public static <T> T parseByPath(String json, String path, JavaType javaType) {
        try {
            JsonNode node = readNodeByPath(json, path);
            if (node == null) {
                return null;
            }

            return mapper.readValue(mapper.treeAsTokens(node), javaType);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            throw new APIRuntimeException("json转换异常", e);
        }
    }

    private static JsonNode readNodeByPath(String json, String path) throws IOException {
        if (StringUtils.isEmpty(json)) {
            return null;
        }

        if (StringUtils.startsWith(path, ROOT_PATH_PRE)) {
            path = path.substring(ROOT_PATH_PRE.length());
        }

        JsonNode node = mapper.readTree(json);
        if (StringUtils.isEmpty(path) || path.equals(ROOT_PATH)) {
            return node;
        }

        String[] fieldsArray = path.split("\\.");
        if (!ArrayUtils.isEmpty(fieldsArray)) {
            for (String field : fieldsArray) {
                if (node == null) {
                    break;
                }
                node = node.get(field);
            }
        }
        return node;
    }

    public static JsonNode readTree(String jsonStr) {
        try {
            return readNodeByPath(jsonStr, null);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new APIRuntimeException("json转换异常", e);
        }
    }
}
